using System;
using System.IO;
using System.IO.Ports;
using System.Text;
using System.Threading;

namespace KukaDataCom
{
   public class MySerialPort
   {
      #region Eigenschaften

      private SerialPort port;

      public string PortName
      {
         get
         {
            if (port == null)
               return String.Empty;
            return port.PortName;
         }
         set
         {
            if (port != null)
            {
               closePort();
            }
            port = new SerialPort(value, 57600, Parity.None, 8, StopBits.One);
            openPort();
            OnPortNameChanged();
         }
      }

      public bool isOpen
      {
         get { return (port != null && port.IsOpen); }
      }

      public int ReadTimeout
      {
         get { return port.ReadTimeout; }
      }

      #endregion

      #region Konstruktor

      public MySerialPort()
         : this(null)
      {
      }

      public MySerialPort(string portName)
      {
         if (portName == null)
         {
            /*
                string[] ports = SerialPort.GetPortNames();
                if (ports.Length > 0)
                {
                    portName = ports[ports.Length - 1];
                }
                */
            port = null;
         }
         else
         {
            try
            {
               PortName = portName;
            }
            catch (IOException)
            {
            }
         }
      }

      #endregion

      #region PortControll

      private void openPort()
      {
         if (port != null && !port.IsOpen)
         {
            try
            {
               port.Open();
               port.ReadTimeout = 700;
            }
            catch (IOException)
            {
               port = null;
               throw;
            }
         }
      }

      private void closePort()
      {
         if (port != null)
         {
            port.Close();
         }
      }

      #endregion

      #region events

      public event Action<string> PortNameChanged;

      protected void OnPortNameChanged()
      {
         if (PortNameChanged != null)
         {
            PortNameChanged(PortName);
         }
      }

      public event Action<string> SomethingHappend;

      protected void OnSomethingHappend(string message)
      {
         if (SomethingHappend != null)
            SomethingHappend(message);
      }

      #endregion

      #region public Methods

      public void Write(char c)
      {
         if (isOpen)
         {
            OnSomethingHappend("Tx: " + c);
            Write(Encoding.ASCII.GetBytes(new[] {c}));
         }
      }

      public void Write(byte[] bytes)
      {
         if (isOpen)
         {
            var text = new StringBuilder("Tx: byte[");
            text.Append(bytes.Length);
            text.Append("]: ");
            foreach (byte b in bytes)
               text.Append(string.Format("{0:X} ", b));
            Thread.Sleep(50);
            OnSomethingHappend(text.ToString());
            port.Write(bytes, 0, bytes.Length);
         }
      }

      public char ReadChar()
      {
         if (isOpen)
         {
            try
            {
               var byteRead = new[] {(byte) port.ReadByte()};
               char[] charRead = Encoding.ASCII.GetChars(byteRead);

               if (charRead.Length == 1)
               {
                  OnSomethingHappend("Rx: " + charRead[0]);
                  return charRead[0];
               }
            }
            catch (TimeoutException)
            {
               OnSomethingHappend("Err: ReadChar() timed out after " + port.ReadTimeout + "ms.");
            }
            catch(IOException)
            {
               OnSomethingHappend("IO not available.");
            }
         }
         return char.MinValue;
      }

      public byte[] ReadBytes()
      {
         return ReadBytes(512);
      }

      public byte[] ReadBytes(int bufferSize)
      {
         if (isOpen && bufferSize > 0)
         {
            var buffer = new byte[bufferSize];
            Thread.Sleep(1000);
            int length = port.Read(buffer, 0, buffer.Length);
            OnSomethingHappend("Rx: byte[" + length + "]");
            if (length == buffer.Length)
               return buffer;
            var result = new byte[length];
            Array.Copy(buffer, 0, result, 0, result.Length);
            return result;
         }
         return new byte[0];
      }

      public bool WaitFor(char c)
      {
         if (isOpen)
         {
            OnSomethingHappend("Waiting for " + c);
            while (ReadChar() != c)
            {
            }
            return true;
         }
         return false;
      }

      #endregion
   }
}